#!/usr/bin/env python


import os
import sys
import cmd
import socket
from optparse import OptionParser

from dexen.system.common import service
from dexen.system.common.meta import ScoreMeta
from dexen.system.common.states import JobState
from dexen.system.client.proxy import ServerProxy, JobProxy
from dexen.system.common.excepts import (AllNodeBusyException,
                                         DexenConnectionError,
                                         DexenRegistryDiscoveryError)

#===============================================================================
# Constants
#===============================================================================
MY_EXIT = -999
ARG_ERR_MSG = "Incorrect number of arguments\n"

#===============================================================================
# MyCmd
#===============================================================================
class MyCmd(cmd.Cmd):
    def __init__(self, server):
        cmd.Cmd.__init__(self)
        #self.server = ServerProxy()
        assert isinstance(server, ServerProxy)
        self.server = server
        self.prompt = "DEXEN>"
        self.jobs = {} #job_id:JobProxy
        
    def _get_job(self, job_id):
        if self.jobs.has_key(job_id):
            return self.jobs[job_id]
        job = self.server.get_job(job_id)
        assert job != None
        self.jobs[job_id] = job
        return job    
                    
    #Command: archive_server_addr / asa
    def do_asa(self, line):
        self.do_archive_server_addr(line)
        
    def do_archive_server_addr(self, line):
        parser = OptionParser()
        parser.prog = "archive_server_addr"
        (options, args) = parser.parse_args(line.split())
        if len(args) != 0:
            parser.error(ARG_ERR_MSG)
        ip, port, archive_dir = self.server.get_address()
        msg = ""
        msg += "%s%s\n"%("archive server ip:".ljust(25),ip)
        msg += "%s%s\n"%("archive server port:".ljust(25), port)
        msg += "%s%s\n"%("archive server folder:".ljust(25), archive_dir)
        print msg
        print ""
        
    #Command: start_new_job / snj
    def do_snj(self, line):
        self.do_start_new_job(line)
        
    def do_start_new_job(self, line):
        self.server.check_connection()
        parser = OptionParser()
        parser.prog = "start_new_job"
        parser.set_usage("start_new_job JOB_NAME JOB_DEF_DIR")
        options, args = parser.parse_args(line.split())
        job_dir=os.getenv("JOB_DEF_DIR")
        if len(args) == 2:
            job_name = args[0]
            job_dir = args[1]
        elif len(args) == 1 and job_dir:
            job_name = args[0]
        else:
            parser.error(ARG_ERR_MSG)
        if not job_name.isalnum():
            parser.error("Job name should only contain alphanumeric characters.")
        if not os.path.exists(job_dir):
            parser.error("The specified path for the JOB_DEF does not exist.")
        self.server.start_job(job_name, job_dir)
        print "Job has been started."
        print ""
        
    #Command: stop_job / sj
    def do_sj(self, line):
        self.do_stop_job(line)
        
    def do_stop_job(self, line):
        self.server.check_connection()
        parser = OptionParser("stop_job JOB_ID")
        parser.prog = "stop_job"
        (options, args) = parser.parse_args(line.split())
        if len(args) != 1:
            parser.error(ARG_ERR_MSG)
        job = self._get_job(int(args[0]))
        job.stop()
        #TODO: where is the print statement that prints "Job is already stopped."
        print "Job has been stopped."
        print ""
        
    #Command: del_job / dj
    def do_dj(self, line):
        self.do_del_job(line)
        
    def do_del_job(self, line):
        self.server.check_connection()
        parser = OptionParser("del_job JOB_ID")
        parser.prog = "del_job"
        (options, args) = parser.parse_args(line.split())
        if len(args) != 1:
            parser.error(ARG_ERR_MSG)
        job_id = int(args[0])
        job = self._get_job(job_id)
        job.delete()
        del self.jobs[job_id]
        print "Job has been deleted."
        print ""
    
    #Command: list_jobs / lj
    def do_lj(self, line):
        self.do_list_jobs(line)
    
    def do_list_jobs(self, line):
        self.server.check_connection()
        parser = OptionParser()
        parser.prog = "list_jobs"
        jobs = self.server.get_job_states()
        if len(jobs) > 0:
            job_state = jobs[0]
            print job_state.get_header()
        else:
            print "No jobs exist."
        for job_state in jobs:
            assert isinstance(job_state, JobState)
            print job_state
        print ""
        print ""

    #Command: get_job_info / gji
    def do_gji(self, line):
        self.do_get_job_info(line)

    def do_get_job_info(self, line):
        parser = OptionParser("get_job_info JOB_ID")
        parser.prog = "get_job_info"
        (options, args) = parser.parse_args(line.split())
        if len(args) != 1:
            parser.error(ARG_ERR_MSG)
        job = self._get_job(int(args[0]))
        assert isinstance(job, JobProxy)
        job_state = job.get_job_info()
        assert isinstance(job_state, JobState)
        print "\n", job_state.get_info(), "\n"
            
    #Command: get_job_log_file / gjlf
    def do_gjlf(self, line):
        self.do_get_job_log_file(line)
        
    def do_get_job_log_file(self, line):
        parser = OptionParser("get_job_log_file JOB_ID FILE_PATH")
        parser.prog = "get_job_log_file"
        (options, args) = parser.parse_args(line.split())
        if len(args) != 2:
            parser.error(ARG_ERR_MSG)
        job = self._get_job(int(args[0]))
        f = open(args[1], "w")
        f.write(job.get_action_log())
        f.close()
        print "Log file has been created."
        print ""
    
    # Command: get_job_output_file / gjof
    def do_gjof(self, line):
        self.do_get_job_output_file(line)
    
    def do_get_job_output_file(self, line):
        parser = OptionParser("get_job_output_file JOB_ID FILE_PATH")
        parser.prog = "get_job_output_file"
        (options, args) = parser.parse_args(line.split())
        if len(args) != 2:
            parser.error(ARG_ERR_MSG)
        job = self._get_job(int(args[0]))
        assert isinstance(job, JobProxy)
        f = open(args[1], "w")
        f.write(job.get_output_log())
        f.close()
        print "Output file has been created."
        print ""
        
    #Command: get_job_pareto_file / gjpf
    def do_gjpf(self, line):
        self.do_get_job_pareto_file(line)
    
    def do_get_job_pareto_file(self, line):
        parser = OptionParser("get_job_pareto_file JOB_ID FILE_PATH [-m MAX_LEVEL]")
        parser.prog = "get_job_pareto_file"
        parser.add_option("-m", "--max-level", metavar="max_level", dest="max_level",
                          default=1, type="int", 
                          help="rank until this level. Default max_level is 1.")
        (options, args) = parser.parse_args(line.split())
        if len(args) != 2:
            parser.error(ARG_ERR_MSG)
        job = self._get_job(int(args[0]))
        f = open(args[1], "w")
        f.write(job.get_pareto_log(options.max_level))
        f.close()
        print "Pareto file has been created."
        print ""
    
    #Command: get_job_snapshot_file / gjsf
    def do_gjsf(self, line):
        self.do_get_job_snapshot_file(line)
    
    def do_get_job_snapshot_file(self, line):
        parser = OptionParser("get_job_snapshot_file JOB_ID FILE_PATH STEP")
        parser.prog = "get_job_snapshot_file"
        (options, args) = parser.parse_args(line.split())
        if len(args) != 3:
            parser.error(ARG_ERR_MSG)
        job = self._get_job(int(args[0]))
        assert isinstance(job, JobProxy)
        f = open(args[1], "w")
        f.write(job.get_progress_log(int(args[2])))
        f.close()
        print "Snapshot file has been created.\n"
    
    #Command: get_job_advancement_file / gjaf
    def do_gjaf(self, line):
        self.do_get_advancement_file(line)
    
    def do_get_advancement_file(self, line):
        parser = OptionParser("get_job_advancement_file JOB_ID FILE_PATH STEP")
        parser.prog = "get_job_advancement_file"
        (options, args) = parser.parse_args(line.split())
        if len(args) != 3:
            parser.error(ARG_ERR_MSG)
        job = self._get_job(int(args[0]))
        assert isinstance(job, JobProxy)
        f = open(args[1], "w")
        try:
            f.write(job.get_advancement_log(int(args[2])))
        except:
            import traceback
            traceback.print_tb(sys.exc_info()[2])
        finally:
            f.close()
        print "Advancement file has been created.\n"
        
    
    #Command: exit / e
    def do_e(self, line):
        self.do_exit(line)
        
    def do_exit(self, line):
        sys.exit(MY_EXIT)
    
    #Command: help / h
    def do_h(self, line):
        self.do_help(line)
        
    def do_help(self, line):
        msg = "%s\n"%(" DEXEN CLI COMMAND LIST ".center(80,'-'))
        msg += "%sGet the archive IP and port number.\n\n"%("archive_server_addr".ljust(40))
        msg += "%sStart a new job on the server.\n\n"%("start_new_job JOB_NAME JOB_DIR_PATH".ljust(40))
        msg += "%sStop a job running on the server.\n\n"%("stop_job JOB_ID".ljust(40))
        msg += "%sDelete a job on the server.\n\n"%("del_job JOB_ID".ljust(40))
        msg += "%sList all the jobs on the server.\n\n"%("list_jobs".ljust(40))
        msg += "%sCreate a log file for a job.\n\n"%("get_job_log_file JOB_ID FILE_PATH".ljust(40)) 
        msg += "%sCreate an output file for a job.\n\n"%("get_job_output_file JOB_ID FILE_PATH".ljust(40)) 
        msg += "%sCreate a pareto file for a job.\n\n"%("get_job_pareto_file JOB_ID FILE_PATH".ljust(40))
        msg += "%sCreate a snapshot file for a job.\n\n"%("get_job_snapshot_file JOB_ID FILE_PATH STEP".ljust(40))
        msg += "%sCreate an advancement file for a job.\n\n"%("get_job_advancement_file JOB_ID FILE_PATH STEP".ljust(40))
        msg += "%sExit the CLI.\n\n"%("exit".ljust(40))
        msg += "Commands can also be specified using a short form, "
        msg += "where only the first letter of each word is used. "
        msg += "For example, the short form of 'start_new_job' is 'snj'.\n\n"
        msg += "%s"%("...".center(80,'-'))
        print msg
    
#===============================================================================
# Main
#===============================================================================
def start():
    try:
        c.cmdloop("")
    except SystemExit, e:
        if e.code == MY_EXIT:
            print "Exiting.\n"
        else:
            start()
    except KeyboardInterrupt:
        print "Keyboard interrupted.\n"
        print ""
    except socket.error:
        print "Server is not reachable.\n"
        print ""
        start()        
    except EOFError:
        print "EOFError: ServerDisconnected.\n"
        print ""
        start()
    except Exception, e:
        print e
        if isinstance(e, AllNodeBusyException):
            print "Please start a new node or try again later.\n"
        print ""
        start()
    except:
        print "Unknown Exception.\n"
        print ""
        start()

def main():
    global c
    parser = OptionParser()
    parser.prog = "dexen-cli.py"
    parser.add_option("-n", "--server-name", dest="reg_host",
                      default=service.get_my_ip(), metavar="SERVER_NAME",
                      help="Locate the server based on its name or ip.")
    (options, args) = parser.parse_args()
    server = ServerProxy()
    try:
        print "Dexen cli is trying to connect to the server [%s]."\
                                                %(options.reg_host,)
        print "Please wait..."
        server.connect(options.reg_host)
    except socket.error:
        print "Server is not reachable.\n"
        sys.exit()
    except (DexenConnectionError, DexenRegistryDiscoveryError):
        err_msg = "Dexen cli could not connect to the server [%s].\n"%(options.reg_host,) 
        err_msg += "Please check your network and firewall settings make sure "
        err_msg += "that the server is running."
        print err_msg
        sys.exit()
    print "Connected.\n"
    msg = "%s"%(" Welcome to DEXEN CLI ".center(60, '*'))
    print msg 
    c = MyCmd(server)
    start()  

if __name__ == "__main__":
    main()

